﻿namespace NorthernLights.Encryption
{
    using System;
    using System.Text;
    using NorthernLights.Core;

    /// <summary>
    /// ProtectedData class.
    /// </summary>
    public static class ProtectedData
    {
        private static string saltSuffix = "_SALT";

        /// <summary>
        /// Protect data.
        /// </summary>
        /// <typeparam name="T">The object type.</typeparam>
        /// <param name="passphrase">The passphrase to encrypt the data with.</param>
        /// <param name="key">The key to store the data in.</param>
        /// <param name="item">The object to store.</param>
        public static void Protect<T>(string passphrase, string key, T item)
        {
            if (string.IsNullOrEmpty(passphrase) || string.IsNullOrEmpty(key) || item == null)
            {
                return;
            }

            int size = 400;

            byte[] data;
            byte[] password;
            byte[] salt = ByteGenerator.Generate(size);

            // Convert object to JSON.
            string json = Serializer.WriteFromObject<T>(item);

            // Convert JSON to bytes[]
            data = Encoding.UTF8.GetBytes(json);

            // Convert passphrase to bytes[]
            password = Encoding.UTF8.GetBytes(passphrase);

            // Encrypt data.
            byte[] encryptedData = NorthernLights.Encryption.Symmetric.Encryption.Encrypt(data, password, salt);

            // Convert encrypted data to base64.
            string encryptedDataAsBase64 = Convert.ToBase64String(encryptedData);

            // Convert salt to base64.
            string saltAsBase64 = Convert.ToBase64String(salt);

            // Store encrypted data.
            PersistentVariables.Set<string>(key, encryptedDataAsBase64);

            // Store salt.
            PersistentVariables.Set<string>(key + saltSuffix, saltAsBase64);
        }

        /// <summary>
        /// Unprotect data.
        /// </summary>
        /// <typeparam name="T">The object type.</typeparam>
        /// <param name="passphrase">The passphrase to decrypt the data with.</param>
        /// <param name="key">The key to retrieve the data from.</param>
        /// <returns>The decrypted object.</returns>
        public static T Unprotect<T>(string passphrase, string key)
        {
            if (string.IsNullOrEmpty(passphrase) || string.IsNullOrEmpty(key))
            {
                return default(T);
            }

            // Get base64 encoded salt from isolated storage.
            string saltAsBase64 = PersistentVariables.Get<string>(key + saltSuffix);

            // Get base64 encoded encrypted data from isolated storage.
            string encryptedDataAsBase64 = PersistentVariables.Get<string>(key);

            // Make sure values are not empty.
            if (string.IsNullOrEmpty(saltAsBase64) || string.IsNullOrEmpty(encryptedDataAsBase64))
            {
                return default(T);
            }

            // Convert salt to byte[].
            byte[] salt = Convert.FromBase64String(saltAsBase64);

            // Convert encrypted data to byte[].
            byte[] encryptedData = Convert.FromBase64String(encryptedDataAsBase64);

            // Convert passphrase to byte[].
            byte[] password = Encoding.UTF8.GetBytes(passphrase);

            // Decrypt data.
            byte[] data = NorthernLights.Encryption.Symmetric.Encryption.Decrypt(encryptedData, password, salt);

            if (data != null)
            {
                // Convert data byte[] to string and return deserialized object.
                return Serializer.ReadToObject<T>(Encoding.UTF8.GetString(data, 0, data.Length));
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// Deletes ProtectedData from device.
        /// </summary>
        /// <param name="key">The key of the protected data to delete.</param>
        public static void Delete(string key)
        {
            PersistentVariables.Delete(key);
            PersistentVariables.Delete(key + saltSuffix);
        }

        /// <summary>
        /// Indicates whether a ProtectedData storage exists with that key.
        /// </summary>
        /// <param name="key">The key of the protected data to check.</param>
        /// <returns>True if the ProtectedData exists.</returns>
        public static bool Exists(string key)
        {
            return PersistentVariables.Exists(key);
        }
    }
}
